home *** CD-ROM | disk | FTP | other *** search
/ The 640 MEG Shareware Studio 2 / The 640 Meg Shareware Studio CD-ROM Volume II (Data Express)(1993).ISO / clang / stayresc.zip / FDIRTSR.C < prev    next >
Text File  |  1985-10-27  |  14KB  |  361 lines

  1.  
  2. /****************************************************************************/
  3. /*                                        */
  4. /*     FDIRTSR.C   Version 1.0    10/23/85           Brian Irvine        */
  5. /*                                        */
  6. /*      Released to the Public Domain for use without profit            */
  7. /*                                        */
  8. /****************************************************************************/
  9. /*                                        */
  10. /* stayres.c - Code which can be used in a general way to create programs   */
  11. /*           in C which will terminate and stay resident.  This code        */
  12. /*           will allow the use of DOS I/O without having the stack and   */
  13. /*           registers clobbered.  This code is written in DeSmet C and   */
  14. /*           uses library functions which may not be available in other   */
  15. /*           C compilers.  It also makes heavy use of the #asm compiler   */
  16. /*           directive to allow in-line assembly language within the C    */
  17. /*           code.  This code provides a general outline for a main()     */
  18. /*           function which can be modified to suit the users needs. All  */
  19. /*           the code necessary to terminate and stay resident is        */
  20. /*           contained in this module; the user's program can be contain- */
  21. /*           ed entirely externally.    The program does not have to be a   */
  22. /*           COM file, and the amount of memory reserved is not limited   */
  23. /*           to 64K. The code has not been tested on program files with   */
  24. /*           greater than 64K of code.                    */
  25. /*                                        */
  26. /*           This code is based on a set of routines written in Turbo     */
  27. /*           Pascal by Lane H. Ferris which are available on the Borland  */
  28. /*           SIG on Compuserve.                        */
  29. /*                                        */
  30. /*           Brian Irvine                            */
  31. /*           3379 St Marys Place                        */
  32. /*           Santa Clara, CA 95051                        */
  33. /*           [71016,544]                            */
  34. /*                                        */
  35. /****************************************************************************/
  36.  
  37. #include   "stdio.h"
  38.  
  39. /*----- Global variables ---------------------------------------------------*/
  40.  
  41. unsigned   dos_regs [10];           /* save DOS registers here */
  42. unsigned   dos_dseg;               /* save the DS and SS regs */
  43. unsigned   dos_sseg;               /* for later convenience */
  44. unsigned   dos_sp;               /* storage for DOS stack pointer */
  45. unsigned   c_sseg;               /* save our stack segment here */
  46. unsigned   c_sp;               /* and our stack pointer here */
  47. unsigned   stacksize;               /* size of DOS/local stack */
  48. int       _rax, _rbx, _rcx, _rdx,     /* variables hold register values */
  49.        _rsi, _rdi, _rds, _res;
  50.  
  51. /*--------------------------------------------------------------------------*/
  52. /* note: storage to save the DOS/C data segments must be allocated in the   */
  53. /*     code segment, so variable must be defined inside "#asm - #" area.  */
  54. /*--------------------------------------------------------------------------*/
  55.  
  56. /*----- Constants ----------------------------------------------------------*/
  57.  
  58. #define    KB_INT      0x16    /* BIOS software int keyboard service routine */
  59. #define    DOS_INT     0x21    /* DOS kitchen sink interrupt */
  60. #define    USER_INT    0x67    /* INT 16H vector is moved to here */
  61.  
  62. #define    WAKEUP      0x71    /* scan code for Alt-F10 */
  63. #define    PARAGRAPHS  0x1000  /* # of paragraphs of memory to keep */
  64.  
  65. /*----- Externals ----------------------------------------------------------*/
  66.  
  67. extern unsigned _PCB;    /* DeSmet C stores original sp value at _PCB + 2. */
  68. extern void    fdir ();          /* user's program module entry point */
  69.  
  70. /*--------------------------------------------------------------------------*/
  71. /*     Initial program startup code                        */
  72. /*--------------------------------------------------------------------------*/
  73.  
  74. void    stayres ()
  75. {
  76.  
  77. /* Save the C data segment and reserve storage in code segment for later use */
  78.  
  79. #asm
  80.  
  81.        jmp       get_ds
  82.  
  83. active_:   db       0
  84. c_ds_:       dw       0
  85. dos_ds_:   dw       0
  86.  
  87. get_ds:    push    ax
  88.        mov       ax,ds
  89.        mov       cs:c_ds_,ax
  90.        pop       ax
  91.  
  92. #
  93.    /* initialize program stack segment variable */
  94.  
  95.    c_sseg = _showds();
  96.  
  97.    /* get the current address of INT 16H and save it */
  98.  
  99.    _res = 0;               /* just for DOS 3.0 */
  100.    _rax = 0x3500 + USER_INT;
  101.    _doint (DOS_INT);
  102.  
  103.    if ( _res != 0 )
  104.        puts ("\nInterrupt 67H in use.  Can't install program.\n");
  105.    else
  106.        {
  107.  
  108. /*--------------------------------------------------------------------------*/
  109. /*     Do your program initialization here because you won't get hold of    */
  110. /*     it again until it is entered with the wakeup key sequence.        */
  111. /*--------------------------------------------------------------------------*/
  112.  
  113.        /* get address of current INT 16H routine */
  114.  
  115.        _rax = 0x3500 + KB_INT;
  116.        _doint (DOS_INT);
  117.  
  118.        /* set the old INT 16H code up as INT 67H for later use */
  119.  
  120.        _rax = 0x2500 + USER_INT;
  121.        _rds = _res;
  122.        _rdx = _rbx;
  123.        _doint (DOS_INT);
  124.  
  125.        /*  now set the INT 16H vector to point to our new service routine */
  126.  
  127.        _rax = 0x2500 + KB_INT;
  128.        _rds = _showcs();
  129.  
  130. #asm
  131.        push    bx
  132.        lea       bx, back_door_
  133.        mov       word _rdx_,bx
  134.        pop       bx
  135. #
  136.        _doint (DOS_INT);
  137.  
  138.  
  139. /*----- Now terminate while staying resident -------------------------------*/
  140.  
  141.  
  142. /****************************************************************************/
  143. /*                                        */
  144. /*     note:                                    */
  145. /*     DeSmet C lays claim to the entire 64K of the data segment by using   */
  146. /*     it for the stack segment as well.  The data resides at the bottom    */
  147. /*     of the segment while the stack starts at the top and works down.     */
  148. /*     Because of this arrangement, we must reserve at least 64k of space   */
  149. /*     in addition to the amount of memory occupied by code.  Cware to the  */
  150. /*     rescue, however.  The DeSmet bind program allows you to specify a    */
  151. /*     maximum stack size when the modules are combined at link time. By    */
  152. /*     using the "-shhhh" option, you can specify a stack of 4K, which      */
  153. /*     should be more than adequate for most applications.  The object        */
  154. /*     module of this program must be combined with the "fdires.o" module   */
  155. /*     with a bind command like:                        */
  156. /*                                        */
  157. /*               bind fdires fdirtsr -s1000                */
  158. /*                                        */
  159. /*     The stack size is specified in hex.                    */
  160. /*                                        */
  161. /****************************************************************************/
  162.  
  163.  
  164.        /* calculate how much memory we need to reserve (in paragraphs)      */
  165.        /*                                  */
  166.        /*      size = ds - cs + (sp/16) + 0x10 + 0x10 + 1          */
  167.        /*                                  */
  168.        /* ds - cs gives the number of paragraphs of code in our program.  */
  169.        /* sp/16 gives us a fair idea of where the end of the data segment */
  170.        /* is located, but by the time we get to here, the stack pointer   */
  171.        /* will have been moved around so we need to fudge a little.      */
  172.        /* The first "+ 0x10" is to allow for the program segment prefix,  */
  173.        /* the second "+ 0x10" is a fudge factor, so is the "+ 1" because  */
  174.        /*      (1) _showsp () doesn't show the top of the stack           */
  175.        /*      (2) we must always round up with integer division      */
  176.        /* If you run the program and it seems to operate fine, but      */
  177.        /* when you try to run another program above it you get          */
  178.        /* a DOS message "Memory allocation error can't load COMMAND.COM"  */
  179.        /* it means you haven't allocated enough memory for your program   */
  180.        /* and your stack has destroyed the memory allocation linked list. */
  181.        /* Try recompiling with more memory reserved.              */
  182.  
  183.        _rax = 0x3100;
  184.        _rdx = _showds() - _showcs() + ( _showsp() / 16 ) + 0x20 + 1;
  185.        printf ( "FDIRES now installed.\nPress Alt-F10 to activate.\n", _rdx );
  186.        _doint (DOS_INT);
  187.  
  188.        }
  189.  
  190. }
  191.  
  192.  
  193. /*----- Interrupt 16H service routine --------------------------------------*/
  194. /*                                        */
  195. /*     This function replaces the standard BIOS INT 16H service routine.    */
  196. /*     It is called by DOS and our application program to get the next        */
  197. /*     character from the queue, check the shift status, or just see if     */
  198. /*     there is a character in the queue.  All type 1 and 2 requests are    */
  199. /*     passed through to the original service routine, through a long        */
  200. /*     jump to that address.  Type 0 requests (get the next character)        */
  201. /*     are handled through an INT 67H call to the original service rou-     */
  202. /*     tine.  The character obtained is checked to see if it is the        */
  203. /*     designated wakeup key.  If not, it is passed on to DOS.    If it is,   */
  204. /*     then part of the DOS stack is saved on the local stack, all the        */
  205. /*     processor registers are saved in memory and the program is        */
  206. /*     started up.  The C program environment and registers are estab-        */
  207. /*     lished and the DOS environment is saved for later restoration.        */
  208. /*     Upon return from the user's program, the DOS stack is restored       */
  209. /*     from the data saved on the local stack, bringing DOS back to        */
  210. /*     where it was before the user program was turned on.            */
  211. /*     These operations allow programs to be written in C without having    */
  212. /*     to worry about the resident routines destroying the DOS registers    */
  213. /*     when file I/O is used.  It also allows the use of printf().        */
  214. /*                                        */
  215. /*--------------------------------------------------------------------------*/
  216.  
  217.  
  218.  
  219. void   back_door()
  220. {
  221. /*
  222.    Current stack contents:
  223.            sp -> DOS ip
  224.        sp + 2 -> DOS cs
  225.        sp + 4 -> DOS flags
  226.  
  227.    C function prologue:
  228.        push    bp
  229.        mov       bp,sp
  230. */
  231.  
  232. #asm
  233.        pop       bp               ;toss out the bp from the prologue
  234.        cmp       cs:active_,1        ;if the program is currently active,
  235.        jne       not_on           ;go service the request
  236. ;
  237. ; long jump to F000:E82E
  238. ;
  239.        db       0EAH
  240.        dw       0E82EH
  241.        dw       0F000H
  242. ;
  243. not_on:    cmp       ah,0            ;if this is a character request
  244.        jz       chr_rqst           ;check a little further
  245. ;
  246. ; long jump to F000:E82E
  247. ;
  248.        db       0EAH
  249.        dw       0E82EH
  250.        dw       0F000H
  251. ;
  252. chr_rqst:
  253.        int       67H               ;get the next character from the queue
  254.        cmp       ah,71H           ;if it's not the wakeup character,
  255.        jne       skipall           ;then take it back to the caller
  256.        mov       cs:active_,1        ;else set the 'program active' flag
  257. ;
  258. ;  now we enter the program
  259. ;
  260.        cli                   ;no interrupts for now
  261.        mov       cs:dos_ds_,ds       ;save the DOS data segment
  262.        mov       ds,cs:c_ds_           ;establish our data segment
  263.        push    bp               ;save bp
  264.        mov       bp,offset dos_regs_ ;point to dos_regs
  265.        mov       ds:[bp+0], ax       ;copy the current registers into an
  266.        mov       ds:[bp+2], bx       ;array in the local data segment
  267.        mov       ds:[bp+4], cx
  268.        mov       ds:[bp+6], dx
  269.        pop       ds:[bp+8]           ;get the bp and save it
  270.        mov       ds:[bp+10], si
  271.        mov       ds:[bp+12], di
  272.        push    ax
  273.        mov       ax, cs:dos_ds_      ;save the DOS ds reg
  274.        mov       ds:[bp+14], ax
  275.        pop       ax
  276.        mov       ds:[bp+16], es      ;save es
  277.        pushf               ;save the flags too
  278.        pop       ds:[bp+18]
  279.        mov       word dos_sseg_,ss   ;save DOS stack segment
  280.        mov       si,ss           ;if DOS stack segment and C stack
  281.        mov       es,si           ;segment are the same, then use the
  282.        mov       ss,word c_sseg_     ;current stack pointer for saving
  283.        cmp       si,word c_sseg_     ;the registers. If not the same, then
  284.        mov       si,sp           ;we are entering the program so use
  285.        je       keep_sp           ;the starting stack pointer
  286.        mov       si,ss:_PCB_ + 2
  287. keep_sp:   xchg    sp,si           ;save the registers on the stack
  288.                        ;now using local stack (ds = ss)
  289.        push    [bp+0]           ;save ax
  290.        push    [bp+2]           ;save bx
  291.        push    [bp+4]           ;save cx
  292.        push    [bp+6]           ;save dx
  293.                        ;don't save bp
  294.        push    [bp+10]           ;save si
  295.        push    [bp+12]           ;save di
  296.        push    [bp+14]           ;save ds
  297.        push    [bp+16]           ;save es
  298.                        ;don't save flags
  299.        sub       cx,cx           ;now save 40 words or less from the
  300.        sub       cx,si           ;DOS stack onto the current stack
  301.        shr       cx,1            ;If the stack size is less than 40
  302.        cmp       cx,40           ;save 'stacksize' words, else save 40
  303.        jle       under40
  304.        mov       cx,40
  305. under40:   mov       word stacksize_,cx
  306. restack:   push    es:[si]
  307.        inc       si
  308.        inc       si
  309.        loop    restack
  310.        push    si               ;save the count of words pushed on stack
  311.        mov       word c_sp_,sp       ;save current stack pointer
  312.        sti
  313.        push    bp               ;do the C function prologue here
  314.        mov       bp,sp
  315.  
  316. #
  317.  
  318. /*----- Call your program from here ----------------------------------------*/
  319.  
  320.        fdir ();
  321.  
  322. /*----- Restore everything -------------------------------------------------*/
  323.  
  324. #asm
  325. restore:
  326.        pop       bp
  327.        cli                   ;no interrupts
  328.        mov       sp,word c_sp_       ;get the stack pointer back
  329.        pop       si               ;get pointer to top of words to be moved
  330.        mov       cx,word stacksize_  ;get count of words to move
  331.        mov       es,word dos_sseg_   ;point es to caller's stack
  332. unstack:   dec       si               ;back up one word
  333.        dec       si
  334.        pop       es:[si]           ;restore the caller's stack from
  335.        loop    unstack
  336.        mov       bp,si           ;save pointer to top of caller's stack
  337.        pop       es               ;pop registers off the stack
  338.        pop       di               ;don't restore ds just yet
  339.        pop       di
  340.        pop       si
  341.        pop       dx
  342.        pop       cx
  343.        pop       bx
  344.        pop       ax
  345.        mov       sp,bp           ;restore the caller's stack pointer
  346.        mov       ax,0            ;tell caller there was no character
  347.        mov       ss,word dos_sseg_   ;switch back to the caller's stack
  348.        mov       bp,offset dos_regs_
  349.        push    ds:[bp+18]           ;restore the flags from memory
  350.        popf
  351.        mov       bp,ds:[bp+8]        ;restore the caller's bp reg
  352.        mov       cs:active_,0        ;reset the 'program active' flag
  353.        mov       ds,cs:dos_ds_       ;finally restore caller's data segment
  354. skipall:   iret                ;head on back
  355.  
  356. #
  357.  
  358. }
  359.  
  360.  
  361.